package com.redmoon.oa.util;

import java.io.*;

import cn.hutool.core.io.FileUtil;
import cn.js.fan.util.ErrMsgException;
import cn.js.fan.util.StrUtil;
import com.cloudweb.oa.utils.SpringUtil;
import com.cloudwebsoft.framework.util.LogUtil;
import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
/*
Apache POI 版本老旧
import org.apache.poi.xwpf.converter.pdf.PdfConverter;
import org.apache.poi.xwpf.converter.pdf.PdfOptions;
*/
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.jodconverter.core.DocumentConverter;
import org.jodconverter.core.document.DefaultDocumentFormatRegistry;
import org.jodconverter.core.office.OfficeException;

public class Word2PDF {
	// ========Error:文档转换失败：VariantChangeType failed
	// make the following two folders: 
	// "C:\Windows\SysWOW64\config\systemprofile\Desktop"
	// "C:\Windows\System32\config\systemprofile\Desktop". // 在windows2008下面必须手工添加此目录
	// Excel、Word转PDF时，异常com.jacob.com.ComFailException: Invoke of: SaveAs 
	// Office版本使用2007，因为2007提供了一个加载项：Microsoft Save as PDF 或 XPS，可将文档另存为PDF格式
	
	static final int wdDoNotSaveChanges = 0;// 不保存待定的更改。
	static final int wdFormatPDF = 17;// PDF 格式
	static final int ppSaveAsPDF = 32;// PDF 格式

	/**
	 * word转pdf
	 *
	 */
	public static void officeToPdf(String inputFile, String pdfFile) {
		String os = System.getProperty("os.name").toLowerCase();
		LogUtil.getLog(Word2PDF.class).info("convertWordToPdf 当前操作系统：" + os);
		if (os.contains("win")) {
			// Windows操作系统
			windowsOfficeToPdf(inputFile, pdfFile);
		} else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
			// Unix/Linux/Mac操作系统
			// linuxOfficeToPdf(inputFile, pdfFile);

			officeToPdfByLibreOffice(inputFile, pdfFile);
		} else {
			// 未知操作系统
			throw new RuntimeException("不支持当前操作系统: " + os + " 转换文档。");
		}
	}

	/**
	 * documents4j底层是基于微软的vbs脚本进行转换的，只支持windows
	 * 在Linux上会报以下错误
	 * com.documents4j.conversion.msoffice.MicrosoftWordBridge could not be created by a (File, long, TimeUnit) constructor
	 * @param inputFile
	 * @param pdfFile
	 * @return
	 */
	public static boolean windowsOfficeToPdf(String inputFile, String pdfFile) {
		InputStream docxInputStream = null;
		OutputStream outputStream = null;
		try {
			DocumentType documentType;
			String ext = StrUtil.getFileExt(inputFile);
			switch (ext) {
				case "docx":
					documentType = DocumentType.DOCX;
					break;
				case "doc":
					documentType = DocumentType.DOC;
					break;
				case "xlsx":
					documentType = DocumentType.XLSX;
					break;
				case "xls":
					documentType = DocumentType.XLS;
					break;
				default:
					throw new ErrMsgException("格式: " + ext + " 不支持转换为pdf文件");
			}

			docxInputStream = new FileInputStream(inputFile);
			outputStream = new FileOutputStream(pdfFile);
			IConverter converter = LocalConverter.builder().build();
			converter.convert(docxInputStream)
					.as(documentType)
					.to(outputStream)
					.as(DocumentType.PDF).execute();
			converter.shutDown();
		}
		catch (Exception e) {
			LogUtil.getLog(Word2PDF.class).error(e);
			return false;
		} finally {
			if (outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					LogUtil.getLog(Word2PDF.class).error(e);
				}
			}
			if (docxInputStream != null) {
				try {
					docxInputStream.close();
				} catch (IOException e) {
					LogUtil.getLog(Word2PDF.class).error(e);
				}
			}
		}
		return true;
	}

	public static void officeToPdfByLibreOffice(String sourceFilePath, String pdfFilePath) {
		DocumentConverter documentConverter = (DocumentConverter)SpringUtil.getBean("documentConverter");
		File sourceFile = new File(sourceFilePath);
		// 转换成pdf的名称
		// 转换成pdf存放路径
		File pdfFile = new File(pdfFilePath);
		// 开始转换
		try {
			documentConverter.convert(sourceFile)
					.to(pdfFile).as(DefaultDocumentFormatRegistry.PDF)
					.execute();
		} catch (OfficeException e) {
			LogUtil.getLog(Word2PDF.class).error(e);
		}
	}

	/**
	 * 通过libreoffice 实现word转pdf -- linux 环境 需要有 libreoffice 服务
	 * LibreOffice不支持多线程文件转换，问题现象： 开两个窗口，执行相同的命令，命令行会卡住无反应
	 * 如果需并发，可考虑用docker部署libreoffice，java代码调用不同容器的soffice来进行转换
	 */
	public static void linuxOfficeToPdf(String inputFile, String pdfFile) {
		// 获取文件的绝对路径和目标路径
		File file = new File(inputFile);
		String absolutePath = file.getAbsolutePath();
		String parentPath = file.getParent();

		// 构建LibreOffice的命令行工具命令
		// 添加到环境变量 /etc/profile 也不行
		// String command = "soffice --headless --invisible --convert-to pdf " + absolutePath + " --outdir " + parentPath;
		// 注意得用绝对路径
		String command = "/opt/libreoffice7.6/program/soffice --headless --invisible --convert-to pdf " + absolutePath + " --outdir " + parentPath;
		LogUtil.getLog(Word2PDF.class).info(command);

		// 执行转换命令
		try {
			executeLinuxCmd(command);

			// 如果inutFile中含有_temp，则为套红时生成的临时docx文件，需将生成的pdf文件名中的_temp去除
			if (inputFile.contains("_temp")) {
				String pathWithoutExt = inputFile.substring(0, inputFile.lastIndexOf("."));
				File f = new File(pathWithoutExt + ".pdf");
				LogUtil.getLog(Word2PDF.class).info("f.path=" + f.getAbsolutePath());

				String newPath = pathWithoutExt.substring(0, pathWithoutExt.length() - "_temp".length()) + ".pdf";
				LogUtil.getLog(Word2PDF.class).info("newPath=" + newPath);

				/*File newFile = new File(newPath);
				if (newFile.exists()) {
					newFile.delete();
				}
				// 如果新文件名已存在，则操作会失败
				f.renameTo(newFile);*/

				FileUtil.rename(f, newPath, false, true);
			}
		} catch (Exception e) {
			LogUtil.getLog(Word2PDF.class).error(e);
		}
	}

	/**
	 * 执行命令行
	 *
	 * @param cmd 命令行
	 * @return
	 * @throws IOException
	 */
	private static boolean executeLinuxCmd(String cmd) throws IOException {
		// Runtime.getRuntime().exec(命令)-无法运行程序、error=2，没有这样的文件或目录
		Process process = Runtime.getRuntime().exec(cmd);

		// 也无效
		// String[] command = new String[] { "/bin/bash", "-c", cmd};
		// Process process = Runtime.getRuntime().exec(command);
		try {
			process.waitFor();
		} catch (InterruptedException e) {
			LogUtil.getLog(Word2PDF.class).error(e);
			Thread.currentThread().interrupt();
			return false;
		}
		return true;
	}

	
    public static boolean wordToPdfByJacob(String inputFile, String pdfFile) throws ErrMsgException {
		LogUtil.getLog(Word2PDF.class).info("启动Word...");
		long start = System.currentTimeMillis();
		boolean isJacobSeted = false;
		ActiveXComponent app = null;
		try {
			// 此处为通过jacob实现转换
			ComThread.InitSTA();
			isJacobSeted = true;
			app = new ActiveXComponent("Word.Application");
			app.setProperty("Visible", false);

			Dispatch docs = app.getProperty("Documents").toDispatch();
			Dispatch doc = Dispatch.call(docs,//
					"Open", //
					inputFile,// FileName
					false,// ConfirmConversions
					true // ReadOnly
					).toDispatch();

			LogUtil.getLog(Word2PDF.class).info("转换文档到PDF..." + pdfFile);
			File tofile = new File(pdfFile);
			if (tofile.exists()) {
				tofile.delete();
			}
			Dispatch.call(doc,//
					"SaveAs", //
					pdfFile, // FileName
					wdFormatPDF);

			Dispatch.call(doc, "Close", false);
			long end = System.currentTimeMillis();
			LogUtil.getLog(Word2PDF.class).info("转换完成..用时：" + (end - start) + "ms.");
			return true; 
		}
		catch (NoClassDefFoundError e) {
			LogUtil.getLog(Word2PDF.class).error(e);
			throw new ErrMsgException("文档转换失败：jacob 尚未配置！");
		}
		catch (Exception e) {
			LogUtil.getLog(Word2PDF.class).error(e);
			throw new ErrMsgException("文档转换失败：" + e.getMessage());
		} finally {
			if (app != null) {
				app.invoke("Quit", wdDoNotSaveChanges);
			}
			if (isJacobSeted) {
				ComThread.Release();
			}
		}
    }
    
	public static void main(String[] args) {
		String filename = "d:/a.doc";
		String toFilename = filename + ".pdf";
		long start = System.currentTimeMillis();
		officeToPdf(filename, toFilename);
	}
}
